package com.vipboot.platform.system.crud.service.rbac;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.vipboot.framework.core.constant.Const;
import com.vipboot.framework.core.exception.ServiceException;
import com.vipboot.framework.core.util.AssertCheck;
import com.vipboot.framework.core.util.MapStructUtil;
import com.vipboot.framework.core.util.StringConst;
import com.vipboot.framework.core.util.TreeBuildUtil;
import com.vipboot.framework.mybatisplus.database.DataBaseHelper;
import com.vipboot.platform.system.crud.dao.SysDeptDao;
import com.vipboot.platform.system.crud.dao.SysPostDao;
import com.vipboot.platform.system.crud.dao.SysUserDao;
import com.vipboot.platform.system.crud.service.DataScopeCheckService;
import com.vipboot.platform.system.domain.bo.SysDeptBo;
import com.vipboot.platform.system.domain.entity.SysDept;
import com.vipboot.platform.system.domain.entity.SysPost;
import com.vipboot.platform.system.domain.entity.SysUser;
import com.vipboot.platform.system.domain.vo.SysDeptVo;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;

@Slf4j
@RequiredArgsConstructor
@Service
public class SysDeptService {
    private final DataScopeCheckService dataScopeCheckService;
    private final SysDeptDao sysDeptDao;
    private final SysUserDao sysUserDao;
    private final SysPostDao sysPostDao;

    private LambdaQueryWrapper<SysDept> buildQueryWrapper(SysDeptBo bo) {
        LambdaQueryWrapper<SysDept> lqw = Wrappers.lambdaQuery();
//        lqw.eq(SysDept::getDeleteFlag, Constants.DEL_FLAG_NORMAL);
        lqw.eq(ObjectUtil.isNotNull(bo.getDeptId()), SysDept::getDeptId, bo.getDeptId());
        lqw.eq(ObjectUtil.isNotNull(bo.getParentId()), SysDept::getParentId, bo.getParentId());
        lqw.like(StrUtil.isNotBlank(bo.getDeptName()), SysDept::getDeptName, bo.getDeptName());
//        lqw.like(StrUtil.isNotBlank(bo.getDeptCategory()), SysDept::getDeptCategory, bo.getDeptCategory());
        lqw.eq(StrUtil.isNotBlank(bo.getStatus()), SysDept::getStatus, bo.getStatus());
        lqw.orderByAsc(SysDept::getAncestors);
        lqw.orderByAsc(SysDept::getParentId);
        lqw.orderByAsc(SysDept::getSortNum);
        lqw.orderByAsc(SysDept::getDeptId);
        return lqw;
    }

    public List<SysDeptVo> list(SysDeptBo bo) {
        LambdaQueryWrapper<SysDept> lqw = this.buildQueryWrapper(bo);
        return sysDeptDao.listVo(lqw, SysDeptVo.class);
    }

    public boolean save(SysDeptBo bo) {

        if (this.checkExistDeptName(bo)) {
            throw new ServiceException("新增部门'" + bo.getDeptName() + "'失败，部门名称已存在");
        }
        // 如果父节点不为正常状态,则不允许新增子节点
        SysDept parent = sysDeptDao.getById(bo.getParentId());
        if (ObjectUtil.notEqual(parent.getStatus(), Const.StatusNormal)) {
            throw new ServiceException("部门停用,不允许新增子部门");
        }

        SysDept entity = MapStructUtil.convert(bo, SysDept.class);
        AssertCheck.throwIfNull(entity, "添加对象不能为空");
        entity.setAncestors(parent.getAncestors() + StringConst.SEPARATOR + entity.getParentId());
        return sysDeptDao.save(entity);
    }

    @Transactional
    public boolean update(SysDeptBo bo) {

        Long deptId = bo.getDeptId();
        dataScopeCheckService.checkDeptDataScope(deptId);

        if (this.checkExistDeptName(bo)) {
            throw new ServiceException("修改部门'" + bo.getDeptName() + "'失败，部门名称已存在");
        }
        if (bo.getParentId().equals(deptId)) {
            throw new ServiceException("修改部门'" + bo.getDeptName() + "'失败，上级部门不能为自身");
        }
        // 禁用状态
        if (StrUtil.equals(bo.getStatus(), Const.StatusDisable)) {
            if (this.hasNormalChildByDeptId(deptId)) {
                throw new ServiceException("该部门包含未停用的子部门！");
            }
            if (this.checkDeptHasUserByDeptId(deptId)) {
                throw new ServiceException("该部门包含用户,不允许禁用");
            }
        }
        SysDept entity = MapStructUtil.convert(bo, SysDept.class);
        AssertCheck.throwIfNull(entity, "修改对象不能为空");

        SysDept oldEntity = sysDeptDao.getById(bo.getDeptId());
        if (ObjectUtil.notEqual(oldEntity.getParentId(), bo.getParentId())) {
            // 如果是新父部门 则校验是否具有新父部门权限 避免越权
            SysDept newParent = sysDeptDao.getById(bo.getParentId());
            if (ObjectUtil.isNotNull(newParent) && ObjectUtil.isNotNull(oldEntity)) {
                String newAncestors = newParent.getAncestors() + StringConst.SEPARATOR + newParent.getDeptId();
                String oldAncestors = oldEntity.getAncestors();
                entity.setAncestors(newAncestors);
                this.updateDeptChildren(bo.getDeptId(), newAncestors, oldAncestors);
            }
        }
//            // 如果该部门是启用状态，则启用该部门的所有上级部门
//            updateParentDeptStatusNormal(dept);
        return sysDeptDao.updateById(entity);
    }

    public boolean removeById(Long deptId) {
        if (this.hasChildByDeptId(deptId)) {
            throw new ServiceException("存在下级部门,不允许删除");
        }
        if (this.checkDeptHasUserByDeptId(deptId)) {
            throw new ServiceException("部门存在用户,不允许删除");
        }
//        if (this.checkDeptHasPostByDeptId(deptId)) {
//            throw new ServiceException("部门存在岗位,不允许删除");
//        }

        dataScopeCheckService.checkDeptDataScope(deptId);
        return sysDeptDao.removeById(deptId);
    }


    /**
     * 获取未被禁用的部门树列表
     */
    public List<Tree<Long>> tree(SysDeptBo bo) {
        // 只查询未禁用部门
        bo.setStatus(Const.StatusNormal);

        List<SysDeptVo> deptList = this.list(bo);
        if (CollUtil.isEmpty(deptList)) {
            return CollUtil.newArrayList();
        }
        return TreeBuildUtil.build(deptList, (dept, tree) ->
                tree.setId(dept.getDeptId())
                        .setParentId(dept.getParentId())
                        .setName(dept.getDeptName())
                        .setWeight(dept.getSortNum()));
    }

    /**
     * 修改子元素关系
     *
     * @param deptId       被修改的部门ID
     * @param newAncestors 新的父ID集合
     * @param oldAncestors 旧的父ID集合
     */
    private void updateDeptChildren(Long deptId, String newAncestors, String oldAncestors) {
        //select * from SysDeptTable where find_in_set(#{deptId}, ancestors)
        LambdaQueryWrapper<SysDept> lqw = Wrappers.lambdaQuery();
        lqw.apply(DataBaseHelper.findInSet(deptId, "ancestors"));
        List<SysDept> children = sysDeptDao.list(lqw);
        List<SysDept> list = new ArrayList<>();
        for (SysDept child : children) {
            SysDept dept = new SysDept();
            dept.setDeptId(child.getDeptId());
            dept.setAncestors(child.getAncestors().replaceFirst(oldAncestors, newAncestors));
            list.add(dept);
        }
        if (CollUtil.isNotEmpty(list)) {
//            if (baseMapper.updateBatchById(list)) {
//                list.forEach(dept -> CacheUtils.evict(CacheNames.SYS_DEPT, dept.getDeptId()));
//            }
            if (CollUtil.isNotEmpty(list)) {
                sysDeptDao.updateBatchById(list);
            }
        }
    }

    /**
     * 是否存在正常状态的子节点
     *
     * @param deptId 部门ID
     * @return 子部门数
     */
    private boolean hasNormalChildByDeptId(Long deptId) {
        LambdaQueryWrapper<SysDept> lqw = Wrappers.lambdaQuery();
        lqw.eq(SysDept::getStatus, Const.StatusNormal)
                .apply(DataBaseHelper.findInSet(deptId, "ancestors"));
        return sysDeptDao.exists(lqw);
    }


    /**
     * 是否存在子节点
     *
     * @param deptId 部门ID
     * @return 结果
     */
    private boolean hasChildByDeptId(Long deptId) {
        LambdaQueryWrapper<SysDept> lqw = Wrappers.lambdaQuery();
        lqw.eq(SysDept::getParentId, deptId);
        return sysDeptDao.exists(lqw);
    }

    /**
     * 校验部门名称是否唯一(同级)
     *
     * @param param 部门信息
     * @return 结果
     */
    private boolean checkExistDeptName(SysDeptBo param) {
        LambdaQueryWrapper<SysDept> lqw = Wrappers.lambdaQuery();
        lqw.eq(SysDept::getDeptName, param.getDeptName())
                .eq(SysDept::getParentId, param.getParentId())
                .ne(ObjectUtil.isNotNull(param.getDeptId()), SysDept::getDeptId, param.getDeptId());
        return sysDeptDao.exists(lqw);
    }

    /**
     * 检验部门是否存在用户
     *
     * @param deptId 部门ID
     * @return 结果 true 存在 false 不存在
     */
    private boolean checkDeptHasUserByDeptId(Long deptId) {
        LambdaQueryWrapper<SysUser> lqw = Wrappers.lambdaQuery();
        lqw.eq(SysUser::getDeptId, deptId);
        return sysUserDao.exists(lqw);
    }

    /**
     * 检验部门是否存在岗位
     *
     * @param deptId 部门ID
     */
    private boolean checkDeptHasPostByDeptId(Long deptId) {
        LambdaQueryWrapper<SysPost> lqw = Wrappers.lambdaQuery();
        lqw.eq(SysPost::getDeptId, deptId);
        return sysPostDao.exists(lqw);
    }

//    /**
//     * 权限过滤，统计部门使用数量
//     *
//     * @param deptId 部门id
//     */
//    public long countDeptById(Long deptId) {
//        return getBaseMapper().countDeptById(deptId);
//    }

}
